#include "ethernet.h"
#include "shoutcast.h"
#include "enc28j60.h"
#include "main.h"
#include "eeprom_fifo.h"
#include <avr/io.h>
#include <stdio.h>
#include <string.h>
#include <avr/pgmspace.h>

Device 		dev;
MAC_addr	req_mac;
TCP_table	tcp_table[MAX_TCP];
byte		in_buffer[MTU_SIZE];
byte		out_buffer[MTU_SIZE];
UINT16		in_len;
UINT16		out_len;
//in_len = 0x0000;
//out_len = 0x0000;

/*============================ TCP ==============================================*/
void	TCP_Abort( byte index )
{
  tcp_table[index].status = STATUS_ABORT;
  tcp_table[index].flags = TCP_FLAG_RST;
  
  return;
}

void	TCP_Close( byte index )
{
  tcp_table[index].status = STATUS_CLOSE;
  tcp_table[index].flags = tcp_table[index].flags | TCP_FLAG_FIN;
  
  return;
}

byte	TCP_Open(  MAC_addr *dst_mac, IP_addr *dst_ip, UINT16 dst_port, UINT16 src_port )
{
  byte i;
  
  for( i=0x00; i < MAX_TCP; i++) /* find empty entry in tcp_table */
  {
    if( tcp_table[i].status == STATUS_CLOSED )
    { break; }
  }
  
  if( i > MAX_TCP )	/* tcp_table full */
  {return 0x80; }
  
  tcp_table[i].mac			= *(dst_mac);
  tcp_table[i].ip			= *(dst_ip);
  tcp_table[i].port			= dst_port;
  tcp_table[i].local_port	= src_port;
  tcp_table[i].acknum		= 0x00000001;
  tcp_table[i].seqnum		= (unsigned int)0x12FF2810;
  tcp_table[i].flags		= (unsigned char)TCP_FLAG_SYN;
  tcp_table[i].status		= STATUS_OPEN;
  tcp_table[i].time			= 0x00;
  tcp_table[i].error		= 0x00;
  
  TCP_Send( i, 0, 0 );
  
  return 0x00;
}

void	TCP_Send( byte index, UINT16 len, byte options )
{
  tcp_table[index].time = 0x00;
  
  Make_TCP_Header( index, len, options );
  
  ENC28J60_SendPacket( ETH_HEADER_SIZE+IP_HEADER_SIZE+TCP_HEADER_SIZE + len, out_buffer );
  
  tcp_table[index].seqnum += len- options;
  
  return;
}


void	TCP_Service( void )
{
  ETH_Header* in_eth;
  IP_Header* in_ip;
  TCP_Header* in_tcp;
  TCP_Header* out_tcp;
  byte index;
  UINT16 len, head_len;
  
  in_eth = (ETH_Header *) &in_buffer[ETH_OFFSET];
  in_ip = (IP_Header *) &in_buffer[IP_OFFSET];
  in_tcp = (TCP_Header *) &in_buffer[TCP_OFFSET];
  
  head_len = ((in_ip->ver_len&0x0F)<<2) + (in_tcp->len<<2); /*header length, mul by 4 to get byte count*/
  len = swap16(in_ip->len) - head_len;
  
  for( index = 0; index < MAX_TCP; index++ )	/*scan tcp_table*/
  {
    if( tcp_table[index].status != STATUS_CLOSED &&
	( swap16(in_tcp->src_port) == tcp_table[index].port ) &&
	( swap16(in_tcp->dst_port) == tcp_table[index].local_port) )
	{ break; }
  }
  
  if( index < MAX_TCP )
  { 	  
		  tcp_table[index].time = 0x00;
    
		switch(tcp_table[index].status)
		{
			case STATUS_OPEN:	/*------------------------------------------- open, wait for syn & ack*/
			  if( (in_tcp->flags&TCP_FLAG_RST) || (in_tcp->flags&TCP_FLAG_FIN) )
			  {
				tcp_table[index].status = STATUS_CLOSED;
			  }
			  else if( in_tcp->flags&TCP_FLAG_SYN && in_tcp->flags&TCP_FLAG_ACK )
			  {
				tcp_table[index].acknum = swap32(in_tcp->seq_num) + len + 1;
				tcp_table[index].seqnum = swap32(in_tcp->ack_num );
				tcp_table[index].flags = TCP_FLAG_ACK;
				tcp_table[index].status = STATUS_OPENED;
				TCP_Send( index, 0, 0);
	
				switch(in_tcp->dst_port)
					{
					  case 80:	/* http */
					break;
					  case 8000: /* shoutcast */
					  len = Shoutcast_Data(index, &in_buffer[ETH_HEADER_SIZE + IP_HEADER_SIZE + (in_tcp->len>>2)],0 , &out_buffer[TCP_DATA_START]);
					  TCP_Send(index, len, 0 );
					break;
					}
			  }
			  else
			  {
				tcp_table[index].acknum = swap32(in_tcp->seq_num) + len + 1;
				tcp_table[index].seqnum = swap32(in_tcp->ack_num );
				tcp_table[index].flags = TCP_FLAG_RST;
				TCP_Send( index, 0, 0);
				tcp_table[index].status = STATUS_CLOSED;
			  }
      
			  break;
		
			  /*----------------------------------------------------------- opened*/
		case STATUS_OPENED:
			if( in_tcp->flags & TCP_FLAG_RST )	/* abort connection */
			{
			  tcp_table[index].status = STATUS_CLOSED;
			}
			else if( in_tcp->flags & TCP_FLAG_FIN )	/* close connection*/
			{
			  tcp_table[index].acknum = swap32( in_tcp->seq_num) + len + 0x00000001; /* update acknum */
			  tcp_table[index].flags = TCP_FLAG_FIN | TCP_FLAG_ACK ; /* ack to fin*/
			  tcp_table[index].status = STATUS_FIN;
	  
			  TCP_Send( index, 0x0000, 0 );
			}
			else if( in_tcp->flags & TCP_FLAG_SYN )	/* open connection */
			{
			  tcp_table[index].acknum = swap32( in_tcp->seq_num ) + len + 0x00000001;
			  tcp_table[index].flags = TCP_FLAG_SYN | TCP_FLAG_ACK ;
	  
			  out_tcp = (TCP_Header *) &out_buffer[TCP_OFFSET];
			  out_tcp->options[0] = 0x02; /* Mximum Segment Size*/
			  out_tcp->options[1] = 0x04; /* len */
			  out_tcp->options[2] = (swap16(TCP_MSS))&0x00FF;
			  out_tcp->options[3] = (swap16(TCP_MSS)>>8)&0x00FF;
	  
			  TCP_Send( index, 4, 4 );
	  
			  tcp_table[index].seqnum++;
			  tcp_table[index].status = STATUS_OPENED;
			}
			else
			{
			  if( in_tcp->seq_num != tcp_table[index].acknum )	/* packet lost*/
			  {
				if( in_tcp->seq_num < tcp_table[index].acknum )	/* duplicated frame, send ack */
				{
				  UINT32 ack;
				  ack = tcp_table[index].acknum;
				  tcp_table[index].acknum = swap32(in_tcp->seq_num)+len;
				  tcp_table[index].flags = TCP_FLAG_ACK;
				  TCP_Send( index, 0, 0);
				  tcp_table[index].acknum = ack;
				}
				else						/* frame lost, send acknum*/
				{
				  tcp_table[index].flags = TCP_FLAG_ACK;
				  TCP_Send( index, 0, 0);
				}
			  }
			  else
			  {
				tcp_table[index].acknum = swap32(in_tcp->seq_num) + len;
				tcp_table[index].flags = TCP_FLAG_ACK;
				tcp_table[index].error = 0x00;
				switch(in_tcp->dst_port)
				{
				  case 80:	/* http */
				break;
				  case 7500:
				  len = Shoutcast_Data(index, &in_buffer[ETH_HEADER_SIZE + IP_HEADER_SIZE + (in_tcp->len>>2)], len, &out_buffer[TCP_DATA_START]);
				  TCP_Send(index, len, 0 );
				break;
				}
			  }
			}
    
			break;
      
			case STATUS_ABORT:
				tcp_table[index].acknum = swap32(in_tcp->seq_num) + len + 1;
				tcp_table[index].seqnum = swap32(in_tcp->ack_num );
				tcp_table[index].flags = TCP_FLAG_RST;
				TCP_Send( index, 0, 0);
				tcp_table[index].status = STATUS_CLOSED;
	
		break;
      
		case STATUS_CLOSE:
			if( in_tcp->flags&TCP_FLAG_RST )
			  {
				tcp_table[index].status = STATUS_CLOSED;
			  }
			  else if(in_tcp->flags&TCP_FLAG_FIN )
			  {
			tcp_table[index].acknum = swap32(in_tcp->seq_num) + len + 1;
			tcp_table[index].flags = TCP_FLAG_ACK ;
			TCP_Send( index, 0, 0);
			tcp_table[index].status = STATUS_FIN;
			  }
			  else /*im closing*/
			  {
			tcp_table[index].acknum = swap32(in_tcp->seq_num) + len;
			tcp_table[index].flags = TCP_FLAG_ACK | TCP_FLAG_FIN ;
			TCP_Send( index, 0, 0);
			tcp_table[index].seqnum++;
			tcp_table[index].status = STATUS_FIN;
			  }
      
			  break;
      
			case STATUS_FIN:
			  if( in_tcp->flags&TCP_FLAG_RST )
			  {
				tcp_table[index].status = STATUS_CLOSED;
			  }
				else if( in_tcp->flags&TCP_FLAG_ACK && in_tcp->flags&TCP_FLAG_FIN )
			  {
				tcp_table[index].acknum = swap32(in_tcp->seq_num) + len;
				tcp_table[index].flags = TCP_FLAG_ACK | TCP_FLAG_FIN ;
				TCP_Send( index, 0, 0);
				tcp_table[index].seqnum++;
				tcp_table[index].status = STATUS_CLOSED;
			  }
			  else if(in_tcp->flags&TCP_FLAG_FIN)
			  {
				tcp_table[index].acknum = swap32(in_tcp->seq_num) + len +1;
				tcp_table[index].flags = TCP_FLAG_ACK;
				TCP_Send( index, 0, 0);
				tcp_table[index].seqnum++;
				tcp_table[index].status = STATUS_CLOSED;
			  }
      
			  break;
	  }
  }  
  else /*new con*/
  {
    for(index = 0; index < MAX_TCP; index++)
    {
      if(tcp_table[index].status == STATUS_CLOSED) {break;}
    }
    
    if( index < MAX_TCP )
    {
      if( in_tcp->flags&TCP_FLAG_SYN ) /*im server*/
      {
		tcp_table[index].mac.b8[0] = in_eth->src_mac.b8[0];
		tcp_table[index].mac.b8[1] = in_eth->src_mac.b8[1];
		tcp_table[index].mac.b8[2] = in_eth->src_mac.b8[2];
		tcp_table[index].mac.b8[3] = in_eth->src_mac.b8[3];
		tcp_table[index].mac.b8[4] = in_eth->src_mac.b8[4];
		tcp_table[index].mac.b8[5] = in_eth->src_mac.b8[5];
		tcp_table[index].ip.b32 = in_ip->src_ip.b32;
		tcp_table[index].port = swap16(in_tcp->src_port);
		tcp_table[index].local_port = swap16(in_tcp->dst_port);
		tcp_table[index].acknum = swap32(in_tcp->seq_num)+1;
		tcp_table[index].seqnum = (unsigned int)0x12FF2810;
		tcp_table[index].flags = (unsigned char)TCP_FLAG_ACK | TCP_FLAG_SYN;
		tcp_table[index].status = STATUS_OPENED;
		tcp_table[index].time = 0x00;
		tcp_table[index].error = 0x00;
	
		out_tcp = (TCP_Header *) &out_buffer[TCP_OFFSET];
		out_tcp->options[0] = 0x02; /* Mximum Segment Size*/
		out_tcp->options[1] = 0x04; /* len */
		out_tcp->options[2] = (swap16(TCP_MSS))&0x00FF;
		out_tcp->options[3] = (swap16(TCP_MSS)>>8)&0x00FF;
	  
		TCP_Send( index, 4, 4 );
	
		tcp_table[index].seqnum++;
	  }
	 else if( in_tcp->flags&TCP_FLAG_FIN )
	  {
		tcp_table[index].mac.b8[0] = in_eth->src_mac.b8[0];
		tcp_table[index].mac.b8[1] = in_eth->src_mac.b8[1];
		tcp_table[index].mac.b8[2] = in_eth->src_mac.b8[2];
		tcp_table[index].mac.b8[3] = in_eth->src_mac.b8[3];
		tcp_table[index].mac.b8[4] = in_eth->src_mac.b8[4];
		tcp_table[index].mac.b8[5] = in_eth->src_mac.b8[5];
		tcp_table[index].ip.b32 = in_ip->src_ip.b32;
		tcp_table[index].port = swap16(in_tcp->src_port);
		tcp_table[index].local_port = swap16(in_tcp->dst_port);
		tcp_table[index].acknum = swap32(in_tcp->seq_num)+1;
		tcp_table[index].seqnum = (unsigned int)0x12FF2810;
		tcp_table[index].flags = (unsigned char)TCP_FLAG_ACK | TCP_FLAG_FIN;
		tcp_table[index].status = STATUS_FIN;
		tcp_table[index].time = 0x00;
		tcp_table[index].error = 0x00;
	
		out_tcp = (TCP_Header *) &out_buffer[TCP_OFFSET];
		out_tcp->options[0] = 0x02; /* Mximum Segment Size*/
		out_tcp->options[1] = 0x04; /* len */
		out_tcp->options[2] = (swap16(TCP_MSS))&0x00FF;
		out_tcp->options[3] = (swap16(TCP_MSS)>>8)&0x00FF;
	  
		TCP_Send( index, 4, 4 );
		}		
	else	/* abort con*/
	  {
		tcp_table[index].mac.b8[0] = in_eth->src_mac.b8[0];
		tcp_table[index].mac.b8[1] = in_eth->src_mac.b8[1];
		tcp_table[index].mac.b8[2] = in_eth->src_mac.b8[2];
		tcp_table[index].mac.b8[3] = in_eth->src_mac.b8[3];
		tcp_table[index].mac.b8[4] = in_eth->src_mac.b8[4];
		tcp_table[index].mac.b8[5] = in_eth->src_mac.b8[5];
		tcp_table[index].ip.b32 = in_ip->src_ip.b32;
		tcp_table[index].port = swap16(in_tcp->src_port);
		tcp_table[index].local_port = swap16(in_tcp->dst_port);
		tcp_table[index].acknum = swap32(in_tcp->seq_num)+1;
		tcp_table[index].seqnum = (unsigned int)0x12FF2810;
		tcp_table[index].flags = (unsigned char)TCP_FLAG_ACK | TCP_FLAG_RST;
		tcp_table[index].status = STATUS_CLOSED;
		tcp_table[index].time = 0x00;
		tcp_table[index].error = 0x00;
	
		out_tcp = (TCP_Header *) &out_buffer[TCP_OFFSET];
		out_tcp->options[0] = 0x02; /* Mximum Segment Size*/
		out_tcp->options[1] = 0x04; /* len */
		out_tcp->options[2] = (swap16(TCP_MSS))&0x00FF;
		out_tcp->options[3] = (swap16(TCP_MSS)>>8)&0x00FF;
	  
		TCP_Send( index, 4, 4 );
      }
    } 
  }
  
  return;
}

/*==================================================================================*/
/*-------------------------------- ICMP */
void	ICMP_Service( void )
{
  ETH_Header* in_eth;
  IP_Header* in_ip, *out_ip;
  ICMP_Header* in_icmp, *out_icmp;
  UINT16 len;
  
  in_eth = (ETH_Header*) &in_buffer[ETH_OFFSET];
  in_ip =  (IP_Header*) &in_buffer[IP_OFFSET];
  in_icmp = (ICMP_Header*) &in_buffer[ICMP_OFFSET];
  
  out_ip = (IP_Header*) &out_buffer[IP_OFFSET];
  out_icmp = (ICMP_Header*) &out_buffer[ICMP_OFFSET];
  
  //switch(in_icmp->type)
  //{
	  if( in_icmp->type == ICMP_ECHO_REQ )
	  {
    //case ICMP_ECHO_REQ:
      len = swap16(in_ip->len) - IP_HEADER_SIZE - ICMP_HEADER_SIZE;
      
      out_ip->len = swap16( IP_HEADER_SIZE + ICMP_HEADER_SIZE + len );
      out_ip->proto = IP_PROTO_ICMP;
      //Make_IP_Header(&in_eth->src_mac, &in_eth->src_ip );
	  Make_IP_Header( &in_eth->src_mac, &in_ip->src_ip, (IP_HEADER_SIZE+ICMP_HEADER_SIZE+len), IP_PROTO_ICMP);
      
      out_icmp->type = ICMP_ECHO_REP;
      out_icmp->code = 0x00;
      out_icmp->checksum = 0x0000;
      //memcpy(&out_icmp->data, in_icmp->data, len);
      out_icmp->checksum = checksum( (byte*)out_icmp, ICMP_HEADER_SIZE+len );
      
      ENC28J60_SendPacket(IP_HEADER_SIZE+ICMP_HEADER_SIZE+len, out_buffer);
      
      //break;
	  }
}


/*==================================================================================*/
/*-------------------------------- ARP*/
void	ARP_Request( IP_addr *ip_addr )
{
  ETH_Header* out_eth;
  ARP_Header* out_arp;
  
  out_eth = (ETH_Header*) &out_buffer;
  out_arp = (ARP_Header*) &out_buffer;
  
  out_eth->type = ETH_TYPE_ARP;
  Make_ETH_Header(0);
  
  out_arp->hw_type = swap16( 0x0001);
  out_arp->protocol_type = swap16( 0x0800);
  out_arp->hw_len = 0x06;
  out_arp->protocol_len = 0x04;
  out_arp->op = swap16(0x0001); /*request*/
  out_arp->dst_mac.b8[0] = 0x00;
  out_arp->dst_mac.b8[1] = 0x00;
  out_arp->dst_mac.b8[2] = 0x00;
  out_arp->dst_mac.b8[3] = 0x00;
  out_arp->dst_mac.b8[4] = 0x00;
  out_arp->dst_mac.b8[5] = 0x00;
  if( (ip_addr->b32&dev.netmask.b32) != (dev.ip.b32&dev.netmask.b32) ) /*dst address not on local network, use default router IP addr*/
  {
    out_arp->dst_ip.b32 = dev.gateway.b32;
  }
  else
  {
    out_arp->dst_ip.b32 = ip_addr->b32;
  }
  out_arp->src_mac.b8[0] = dev.mac.b8[0];
  out_arp->src_mac.b8[1] = dev.mac.b8[1];
  out_arp->src_mac.b8[2] = dev.mac.b8[2];
  out_arp->src_mac.b8[3] = dev.mac.b8[3];
  out_arp->src_mac.b8[4] = dev.mac.b8[4];
  out_arp->src_mac.b8[5] = dev.mac.b8[5];
  out_arp->src_ip.b32 = dev.ip.b32;
  
  ENC28J60_SendPacket( ETH_HEADER_SIZE+ARP_HEADER_SIZE, out_buffer );
  
  return;
}

MAC_addr*	ARP_GetMac( IP_addr* ip_addr )
{
  int timeout, arp_timeout;
  
  req_mac.b8[0] = 0x00;
  req_mac.b8[1] = 0x00;
  req_mac.b8[2] = 0x00;
  req_mac.b8[3] = 0x00;
  req_mac.b8[4] = 0x00;
  req_mac.b8[5] = 0x00;
  
  ARP_Request( ip_addr );
  
  timeout = get_acttime()+5;
  arp_timeout = get_acttime()+1;
  
  for(;;)
  {
    ETH_Service();
    
    if( req_mac.b8[0] != 0x00	||
	req_mac.b8[1] != 0x00	||
	req_mac.b8[2] != 0x00	||
	req_mac.b8[3] != 0x00	||
	req_mac.b8[4] != 0x00	||
	req_mac.b8[5] != 0x00 )
    {
      break;
    }
    else if( (get_acttime()- arp_timeout ) > 0 )
    {
      arp_timeout = get_acttime() + 1;
      ARP_Request( ip_addr );
    }
    else if( (get_acttime() - timeout) > 0 )
    {
      return 0;
    }
  }
  
  return &req_mac;
}

void	ARP_Service(void)
{
  ETH_Header *in_eth, *out_eth;
  ARP_Header *in_arp, *out_arp;
  
  in_eth = (ETH_Header*) &in_buffer;
  out_eth = (ETH_Header*) &out_buffer;
  in_arp = (ARP_Header*) &in_buffer;
  out_arp = (ARP_Header*) &out_buffer;
  
  if(	(in_arp->hw_type == swap16( 0x0001))	&&
	(in_arp->protocol_type == swap16( 0x0800))&& 
	(in_arp->hw_len	==	0x06)		&&
	(in_arp->protocol_len == 0x04)		&&
	(in_arp->dst_ip.b32	== dev.ip.b32)	)
  {
    switch( (byte)(in_arp->op & 0x0003) )
    {
      case ARP_REQUEST:	/* ARP request*/
      
	out_eth->type = ETH_TYPE_ARP;
	Make_ETH_Header( &in_eth->src_mac);
	
	out_arp->hw_type	= swap16( 0x0001 );
	out_arp->protocol_type	= swap16( 0x0800 );
	out_arp->hw_len		= 0x06;
	out_arp->protocol_len	= 0x04;
	out_arp->op		= swap16( 0x0002 ); /*reply*/
	out_arp->dst_mac.b8[0]	= in_arp->src_mac.b8[0];
	out_arp->dst_mac.b8[1]	= in_arp->src_mac.b8[1];
	out_arp->dst_mac.b8[2]	= in_arp->src_mac.b8[2];
	out_arp->dst_mac.b8[3]	= in_arp->src_mac.b8[3];
	out_arp->dst_mac.b8[4]	= in_arp->src_mac.b8[4];
	out_arp->dst_mac.b8[5]	= in_arp->src_mac.b8[5];
	out_arp->dst_ip.b32	= in_arp->src_ip.b32;
	out_arp->src_mac.b8[0]	= dev.mac.b8[0];
	out_arp->src_mac.b8[1]	= dev.mac.b8[1];
	out_arp->src_mac.b8[2]	= dev.mac.b8[2];
	out_arp->src_mac.b8[3]	= dev.mac.b8[3];
	out_arp->src_mac.b8[4]	= dev.mac.b8[4];
	out_arp->src_mac.b8[5]	= dev.mac.b8[5];
	out_arp->src_ip.b32	= dev.ip.b32;
	
	ENC28J60_SendPacket( ETH_HEADER_SIZE+ARP_HEADER_SIZE, out_buffer );
	break;
	
      case ARP_REPLY:
	req_mac.b8[0] = in_arp->src_mac.b8[0];
	req_mac.b8[1] = in_arp->src_mac.b8[1];
	req_mac.b8[2] = in_arp->src_mac.b8[2];
	req_mac.b8[3] = in_arp->src_mac.b8[3];
	req_mac.b8[4] = in_arp->src_mac.b8[4];
	req_mac.b8[5] = in_arp->src_mac.b8[5];
	break;
    }
  }
}

/*============================================================================================*/
/*------------------------------- UDP */
void	UDP_Service( void )
{
	ETH_Header	*in_eth;
	IP_Header	*in_ip;
	UDP_Header	*in_udp;
	
	in_eth	= (ETH_Header*) &in_buffer[ETH_OFFSET];
	in_ip	= (IP_Header *) &in_buffer[IP_OFFSET];
	in_udp	= (UDP_Header*) &in_buffer[UDP_OFFSET];
	
	
	if( in_udp->dst_port == UDP_PORT )
	{
		/* ? */
	}
}



/*============================================================================================*/
/*------------------------------- checksums & make_headers*/
UINT16	checksumTCP( byte* s, UINT16 len, IP_addr *dst_ip )
{
  UINT32 sum;
  sum = 0;
  
  /*include TCP pseudoheader*/
  sum = sum + dev.ip.b16[0];
  sum = sum + dev.ip.b16[1];
  sum = sum + dst_ip->b16[0];
  sum = sum + dst_ip->b16[1];
  sum = sum + swap16(len);
  sum = sum + swap16(IP_PROTO_TCP);
  for(; len > 1; len-=2 )
  {
    sum = sum + *((UINT16*)s);
    s+=2;
  }
  if( len ) /*leftover byte*/
  {
     sum = sum + *((byte*)s);
  }
  
  while( sum>>0x10 )
  {
    sum = (sum&0xFFFF)+(sum>>16);
  }
  
  return ~sum;
}

UINT16	checksum( byte* s, UINT16 len )
{
  UINT32 sum;
  sum = 0;
	
  for(; len > 1; len-=2 )
  {
    sum = sum + *((UINT16*)s);
    s+=2;
  }
  if( len ) /*leftover byte*/
  {
     sum = sum + *((byte*)s);
  }
  
  while( sum>>16 )
  {
    sum = (sum&0xFFFF)+(sum>>16);
  }
  
  return ~sum;
}

void	Make_TCP_Header(byte index, UINT16 len, byte options)
{
	TCP_Header *out_tcp;
	UINT16 i;
	
	out_tcp = (TCP_Header*)&out_buffer[TCP_OFFSET];
	
	Make_IP_Header( &tcp_table[index].mac, &tcp_table[index].ip, (IP_HEADER_SIZE+TCP_HEADER_SIZE+len), IP_PROTO_TCP );
	
	out_tcp->src_port = swap16(tcp_table[index].local_port);
	out_tcp->dst_port = swap16(tcp_table[index].port);
	out_tcp->seq_num = swap32(tcp_table[index].seqnum);
	if(tcp_table[index].flags & TCP_FLAG_ACK )
	{
		out_tcp->ack_num = swap32(tcp_table[index].acknum);
	}
	else
	{
		out_tcp->ack_num = 0;
	}
	out_tcp->len = (TCP_HEADER_SIZE+options)/4;
	out_tcp->reserved = 0x00;
	out_tcp->flags = tcp_table[index].flags;
	
	if( tcp_table[index].local_port >= 1000	) /* station stream */
	{
		i = fifo_buffer();
		
		if ( i > 0x4096 )
		{
			out_tcp->window = swap16( 2048 );
		}
		else if (i > 2048)
		{
			out_tcp->window =swap16( 1024);
		}
		else if ( i > 512)
		{
			out_tcp->window = swap16(512);
		} 
		else
		{
			out_tcp->window =swap16(256);
		}
	}									
	
	out_tcp->checksum = 0x0000;
	out_tcp->urgent_pointer = 0x0000;
	out_tcp->checksum = checksumTCP((byte*)out_tcp, TCP_HEADER_SIZE+len, &tcp_table[index].ip);
	
return;
}

void	Make_IP_Header(MAC_addr* dst_mac, IP_addr* dst_ip, UINT16 len, UINT16 proto)
{
	IP_Header *out_ip;
	
	out_ip = (IP_Header *) &out_buffer[IP_OFFSET];
	out_ip->proto = proto;
	Make_ETH_Header(dst_mac);
	
	out_ip->ver_len = 0x40 | (IP_HEADER_SIZE>>2);
	out_ip->tos = 0x00;
	out_ip->id = 0x0000;
	out_ip->len = swap16(len);
	out_ip->flag_offs = swap16(0x4000);
	out_ip->ttl = 128;
	out_ip->proto = proto;
	out_ip->checksum = 0x0000;
	out_ip->dst_ip = *dst_ip;
	out_ip->src_ip = dev.ip;
	
	out_ip->checksum = checksum((byte*)out_ip, IP_HEADER_SIZE);
	
	return;
}

void	Make_ARP_Header( MAC_addr* dst_mac, MAC_addr* dst_arp_mac, IP_addr* dst_arp_ip, UINT16 op)
{
	ARP_Header *out_arp;
	
	out_arp = (ARP_Header*) &out_buffer[ARP_OFFSET];
	
	out_arp->protocol_type = ETH_TYPE_ARP;
	Make_ETH_Header(dst_mac);
	
	out_arp->hw_type		= swap16(0x0001); /* ethernet*/
	out_arp->protocol_type	= swap16(0x0800);
	out_arp->hw_len			= 0x06;
	out_arp->op				= op;
	out_arp->dst_ip.b32		= dst_arp_ip->b32;
	out_arp->dst_mac.b8[0]	= dst_arp_mac->b8[0];
	out_arp->dst_mac.b8[1]	= dst_arp_mac->b8[1];
	out_arp->dst_mac.b8[2]	= dst_arp_mac->b8[2];
	out_arp->dst_mac.b8[3]	= dst_arp_mac->b8[3];
	out_arp->dst_mac.b8[4]	= dst_arp_mac->b8[4];
	out_arp->dst_mac.b8[5]	= dst_arp_mac->b8[5];
	out_arp->src_mac.b8[0]	= dev.mac.b8[0];
	out_arp->src_mac.b8[1]	= dev.mac.b8[1];
	out_arp->src_mac.b8[2]	= dev.mac.b8[2];
	out_arp->src_mac.b8[3]	= dev.mac.b8[3];
	out_arp->src_mac.b8[4]	= dev.mac.b8[4];
	out_arp->src_mac.b8[5]	= dev.mac.b8[5];
	out_arp->src_ip.b32		= dev.ip.b32;
	
	return;
}

void	Make_ETH_Header(MAC_addr* dst_mac)
{
	ETH_Header *out_eth;
	
	out_eth = (ETH_Header*)&out_buffer[ETH_OFFSET];
	
	if( dst_mac )
	{
		out_eth->dst_mac.b8[0] = dst_mac->b8[0];
		out_eth->dst_mac.b8[1] = dst_mac->b8[1];
		out_eth->dst_mac.b8[2] = dst_mac->b8[2];
		out_eth->dst_mac.b8[3] = dst_mac->b8[3];
		out_eth->dst_mac.b8[4] = dst_mac->b8[4];
		out_eth->dst_mac.b8[5] = dst_mac->b8[5];
	}
	else
	{
		out_eth->dst_mac.b8[0] = 0x00;
		out_eth->dst_mac.b8[1] = 0x00;
		out_eth->dst_mac.b8[2] = 0x00;
		out_eth->dst_mac.b8[3] = 0x00;
		out_eth->dst_mac.b8[4] = 0x00;
		out_eth->dst_mac.b8[5] = 0x00;
	}
	
	out_eth->src_mac.b8[0] = dev.mac.b8[0];
	out_eth->src_mac.b8[1] = dev.mac.b8[1];
	out_eth->src_mac.b8[2] = dev.mac.b8[2];
	out_eth->src_mac.b8[3] = dev.mac.b8[3];
	out_eth->src_mac.b8[4] = dev.mac.b8[4];
	out_eth->src_mac.b8[5] = dev.mac.b8[5];
	
	return;
}

void	ETH_TimeService( void )
{
	byte index;
	
	#ifndef TCP_MSS
	TCP_Header *out_tcp;
	#endif
	
	for( index = 0; index < MAX_TCP; index++)
	{
		if(tcp_table[index].status == STATUS_CLOSED )
		{
			continue;
		}
		else if (++tcp_table[index].time > TCP_TIMEOUT)
		{
			if(++tcp_table[index].error > TCP_MAX_ERROR )
			{
				tcp_table[index].flags = TCP_FLAG_RST;
				tcp_table[index].status = STATUS_CLOSED;
				TCP_Send(index, 0,0);
			}
			else
			{
				tcp_table[index].time = TCP_TIMEOUT;
				TCP_Send(index, 0, 0);
			}
		}
		
	}
	
	/* udp */
}

void	ETH_Service(void)
{
	UINT16 len;
	ETH_Header *in_eth;
	IP_Header *in_ip;
	
	in_eth = (ETH_Header*) & in_buffer[ETH_OFFSET];
	in_ip  = (IP_Header *) & in_buffer[IP_OFFSET];
	
	len = ENC28J60_ReceivePacket(MTU_SIZE, in_buffer);
	
	if( len == 0x0000 ) /* no pck received*/
	{
		return ;
	}
	
	in_len = len - 0x0004; /* remove CRC, checked by enc*/
	
	switch( in_eth->type )
	{
		case ETH_TYPE_IPs:
			if( in_ip->dst_ip.b32 == dev.ip.b32 )
			{
				switch( in_ip->proto)
				{
					case IP_PROTO_TCP: TCP_Service(); break;
					case IP_PROTO_ICMP: ICMP_Service(); break;
				}
			}
		break;
		
		case ETH_TYPE_ARPs:
			ARP_Service();
		break;
	}
	
	return;
}


/*====================================================================================*/
/*------------------ set functions ---------------------------------------------------*/
void	ETH_SetGateway(IP_addr* gate)
{
	dev.gateway.b32 = gate->b32;
	
	return;
}

void	ETH_SetNetmask(IP_addr* netmask)
{
	dev.netmask.b32 = netmask->b32;
	
	return;
}

void	ETH_SetIp(IP_addr *ip)
{
	dev.ip.b32 = ip->b32;
	
	return;
}

void	ETH_SetMac(MAC_addr* mac)
{
	dev.mac.b8[0] = mac->b8[0];
	dev.mac.b8[1] = mac->b8[1];
	dev.mac.b8[2] = mac->b8[2];
	dev.mac.b8[3] = mac->b8[3];
	dev.mac.b8[4] = mac->b8[4];
	dev.mac.b8[5] = mac->b8[5];
}

void	ETH_Init( void )
{
	in_len = 0x0000;
	
	memset(&dev, 0, sizeof(dev));
	memset(&tcp_table, 0, sizeof(tcp_table));
	
	return;
}

UINT16	swap16( UINT16 i )
{
	UINT16 xi;
	
	xi = (((i<<8)&0xFF00)|((i>>8)&0x00FF));
	
	return xi;
}

UINT32	swap32( UINT32 i )
{
	UINT32 xi;
	
	xi = ( ((i<<24)&0xFF000000) | ((i<<8)&0x00FF0000) | ((i>>8)&0x0000FF00) | ((i>>24)&0x000000FF) );
	
	return xi;
}